package com.smart.speaker.websocket;

import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.tts.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.smart.speaker.config.ModelConfig;
import com.smart.speaker.util.OpusUtils;
import io.github.jaredmdobson.concentus.OpusApplication;
import io.github.jaredmdobson.concentus.OpusEncoder;
import io.github.jaredmdobson.concentus.OpusException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;

import java.util.List;
import java.util.concurrent.CountDownLatch;

@Slf4j
public class SynthesisHandler {

    // 默认音频采样率和通道数
    private static final int DEFAULT_SAMPLE_RATE = 16000;

    private final GenericObjectPool<SpeechSynthesizer> pool;
    private final MessageHandler messageHandler;
    private final OpusEncoder opusEncoder;

    public SynthesisHandler(
            GenericObjectPool<SpeechSynthesizer> pool,
            MessageHandler messageHandler
    ) {
        this.pool = pool;
        this.messageHandler = messageHandler;
        try {
            this.opusEncoder = new OpusEncoder(DEFAULT_SAMPLE_RATE, 1, OpusApplication.OPUS_APPLICATION_AUDIO);
        } catch (OpusException e) {
            log.error("初始化OpusEncoder失败");
            throw new RuntimeException(e);
        }
    }

    public void synthesizer(String message) {
        SpeechSynthesizer synthesizer = null;
        try {
            CountDownLatch latch = new CountDownLatch(1);
            synthesizer = pool.borrowObject();
            var finalSynthesizer = synthesizer;
            ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<>() {
                @Override
                public void onEvent(SpeechSynthesisResult message) {
                    if (message.getAudioFrame() != null) {
                        try {
                            byte[] bytesArray = message.getAudioFrame().array();
                            List<byte[]> opusBytes = OpusUtils.convertPcmToOpus(opusEncoder, bytesArray, DEFAULT_SAMPLE_RATE);
                            for (byte[] bytes : opusBytes) {
                                messageHandler.sendMessage(bytes);
                            }
//                            messageHandler.sendMessage(bytesArray);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                }

                @Override
                public void onComplete() {
                    log.info("synthesizer complete");
                    pool.returnObject(finalSynthesizer);
                    latch.countDown();
                }

                @Override
                public void onError(Exception e) {
                    log.error("synthesizer callback error", e);
                    close(finalSynthesizer);
                    latch.countDown();
                }
            };
            SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                    .model(ModelConfig.Key.timbre)
                    .text(message)
                    .format(SpeechSynthesisAudioFormat.WAV)
                    .sampleRate(DEFAULT_SAMPLE_RATE)
                    .apiKey(ModelConfig.Key.apiKey)
                    .build();
//            synthesizer.updateParamAndCallback(param, callback);
//            synthesizer.streamingCall(message);
//            synthesizer.streamingComplete();
            synthesizer.call(param, callback);
            latch.await();
        } catch (Exception e) {
            log.error("synthesizer error", e);
            close(synthesizer);
        }
    }

    private void close(SpeechSynthesizer synthesizer) {
        if (synthesizer != null) {
            try {
                synthesizer.getSyncApi().close(1000, "bye");
                pool.invalidateObject(synthesizer);
                pool.addObject();
            } catch (Exception ex) {
                log.error("synthesizer close error", ex);
            }
        }
    }
}
